﻿/*
** Xin YUAN, 2021, BSD (2)
*/

////////////////////////////////////////////////////////////////////////////////

#include <sys/eventfd.h>

////////////////////////////////////////////////////////////////////////////////

// server application

typedef void (*server_cleanup_proc)(void*);

static
server_cleanup_proc g_cleanup_proc = NULL;
static
void* g_cleanup_param = NULL;

static
volatile sig_atomic_t g_sigExitFlag = 0;

static
volatile int g_server_event_fd = -1;

static
inline void _set_server_event() noexcept
{
	//flag
	g_sigExitFlag = 1;
	//event fd
	uint64_t v = 5;
	ssize_t ret = ::write(g_server_event_fd, &v, sizeof(uint64_t));
	(void)ret;  //-1, errno
}

//This function will be executed in any thread.
static
void _sig_handler(int signum)
{
	if ( signum == SIGINT || signum == SIGQUIT || signum == SIGTERM ) {
		_set_server_event();
	}
}

//initialize
bool initialize_server_application(void (*cleanup_proc)(void*), void* param) noexcept
{
	g_cleanup_proc = cleanup_proc;
	g_cleanup_param = param;
	//actions
	struct sigaction act = { 0 };
	act.sa_handler = &_sig_handler;
	act.sa_flags = 0;
	//return value : 0 --- succeeded
	if ( ::sigaction(SIGINT, &act, NULL) == -1 ) { //errno
		::perror("Error: set SIGINT failed!");
		return false;
	}
	if ( ::sigaction(SIGQUIT, &act, NULL) == -1 ) { //errno
		::perror("Error: set SIGQUIT failed!");
		return false;
	}
	if ( ::sigaction(SIGTERM, &act, NULL) == -1 ) { //errno
		::perror("Error: set SIGTERM failed!");
		return false;
	}
	//event fd
	g_server_event_fd = ::eventfd(0, EFD_CLOEXEC);
	if ( g_server_event_fd == -1 ) { //errno
		::perror("Error: cannot create event fd!");
		return false;
	}
	return true;
}

//loop
int32_t run_server_loop() noexcept
{
	ssize_t ret = 0;
	while ( !g_sigExitFlag ) {
		//event fd
		uint64_t v;
		ret = ::read(g_server_event_fd, &v, sizeof(uint64_t));  //may block
		if ( ret == -1 ) {
			if ( errno == EINTR ) {
				ret = 0;
				continue;
			}
			break;
		}
		if ( ret != sizeof(uint64_t) ) {
			ret = -1;
			break;
		}
		//wait flag
		constexpr const int tm = 10 * 2;  //ms
		thread_sleep(tm);
	}
	g_sigExitFlag = 0;
	//eventfd
	::close(g_server_event_fd);  //no check
	g_server_event_fd = -1;
	//clean up
	g_cleanup_proc(g_cleanup_param);
	return ret == -1 ? 2 : 0;
}

void set_server_exit_event() noexcept
{
	_set_server_event();
}

////////////////////////////////////////////////////////////////////////////////
